/************************************************************
 *This file is part of Searcj_PRJ.
 *
 * This program is free.
 *
 * @file    create_dict_index.cpp
 * @brief   to create dict
 *
 * @author  Dgg
 * @email   1390713385@qq.com
 * @version 0.0.1.0
 * @date    2024/10/11
 * @license GUN General Public License(GPL)
 *
 * ------------------------------------------------
 * Remark   :Description
 * ------------------------------------------------
 * Change History:
 *    <Date>   |    <Version>    |   <Author>   |   <Description>
 * 2024/10/12  |     0.0.1.0     |      Dgg     |     createfile
 *
 */
#include <unistd.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <thread>

#include "client/create_dict_index/create_dict_index.h"
//#include "client/create_dict_index/configuration.h"
#include "client/create_dict_index/edit_distance.h"

using std::cerr;
using std::cout;
using std::ifstream;
using std::ofstream;
using std::istream;
using std::istringstream;
using std::string;
using std::vector;
using std::set;

typedef void(*init_func)();

dictProducer* dictProducer::_instance = nullptr;

dictProducer *dictProducer::getInstance(const string &dir, splitTool *splitTool)
{
    if(_instance)
        return _instance;
    _instance = new dictProducer(dir, splitTool);

    return _instance;
}

void dictProducer::initialize(){
    cout << "starting search engine" << '\n';
    cout << "initializing search engine" << '\n';
    string pwd = getcwd(NULL, 0);

    //dictProducer dict(pwd, new splitToolCppJieba);

    this->init_dict();
    this->init_index();

    this->showDict();

    this->storeDict(pwd);
}

void dictProducer::init_dict(){
    string dir_dict = _workPath;
    dir_dict += "/data/dict.dat";

    if(is_initialized(dir_dict))
        return;
    cout << "start build dict" << '\n';
    buildDict();
    cout << "dict build complete" << '\n';
}

void dictProducer::init_index() {
    string dir_index = _workPath;
    dir_index += "/data/index.dat";

    if(is_initialized(dir_index))
        return;
    cout << "start build index" << '\n';
    buildIndex();
    cout << "index build complete" << '\n';
}

string dictProducer::getFiles(int i){
    return _files[i];
}

void dictProducer::pushDict(const string & word){
    //检查该单词是否以及存入
    auto it = std::find_if(
        _dict.begin(), _dict.end(),
        [&word](const pair<string, int> &elem)
        { return elem.first == word; });

    if (it != _dict.end())
        it->second++;
    else
        _dict.push_back({word, 1});

    return;
}

dictProducer::dictProducer(const string &dir){}

dictProducer::dictProducer(const string &dir, splitTool *splitTool):
_workPath(dir),
_files(),
_dict(),
_splitTool(splitTool),
_index()
{
    //暂时写绝对路径，后面换成读取配置文件
    string cnFile = dir + "/resource/yuliao/C3-Art0019.txt";
    string enFile = dir + "/resource/yuliao/english.txt";
    string cnStop = dir + "/resource/yuliao/stop_words_zh.txt";
    string enStop = dir + "/resource/yuliao/stop_words_eng.txt";
    _files.push_back(cnFile);
    _files.push_back(enFile);
    _files.push_back(cnStop);
    _files.push_back(enStop);
}

void dictProducer::buildDict(){
    buildEnDict();
    buildCnDict();
}

void dictProducer::buildEnDict(){
    // 打开英文语料和英文停词
    ifstream ifs;
    ifstream ifs_stop;
    ifs.open(getFiles(1).c_str());
    ifs_stop.open(getFiles(3).c_str());

    if (!ifs)cerr << "File open failed !" << "\n";
    cout << "Open successfully !" << "\n";
    cout << "File path = " << getFiles(1) <<"\n";

    if (!ifs_stop)
        cerr << "File open failed !" << "\n";
    cout << "Open successfully !" << "\n";
    cout << "File path = " << getFiles(3) << "\n";

    //洗词，讲所有大写字母换成小写字母，以及去除标点符号
    char ch;
    string yuliao;
    cout << "spliting & washing" << '\n';
    while (ifs.get(ch))
    {
        if (isupper(ch))
        {
            ch = tolower(ch); // 转换为小写
        }
        if (isalpha(ch) || ch == '\'')
        {
            yuliao.push_back(ch);
        }
        else
        {
            yuliao.push_back(' '); // 替换标点为空间
        }
    }

    //切词
    cout << "spliting" << '\n';
    vector<string>words;
    _splitTool->cut(yuliao, words);

    //获取停词
    string temp;
    set<string> stop_word;
    while(ifs_stop >> temp)stop_word.insert(temp);

    //洗词
    cout << "washing" << '\n';
    vector<string> word_clean;
    word_clean.reserve(yuliao.size());

    for(auto &i : words){
        if(stop_word.find(i) == stop_word.end()){
            word_clean.push_back(i);
        }
    }

    istringstream iss(yuliao);
    while(iss >> temp){
        if(stop_word.find(temp) == stop_word.end()){
            word_clean.push_back(temp);
        }
    }

    //统计词频
    cout << "statisticing" << '\n';
    for(auto &i: word_clean){
        auto it = std::find_if(
            _dict.begin(), _dict.end(),
            [&i](const pair<string, int> &elem)
            {
                return elem.first == i;
            });

        if(it != _dict.end())
            it->second++;
        else
            _dict.push_back({i, 1});
    }

    // //统计词频
    // string word;
    // istringstream iss(yuliao);
    // cout << "statisticing" << '\n';
    // while(iss >> word){
    //     auto it = std::find_if(
    //         _dict.begin(), _dict.end(),
    //         [&word](const pair<string, int> &elem)
    //         { return elem.first == word; });

    //     if (it != _dict.end())
    //         it->second++;
    //     else
    //         _dict.push_back({word, 1});
    // }

    ifs.close();
    ifs_stop.clear();
}

void dictProducer::buildCnDict(){
    //打开中文语料和中文停词
    ifstream ifs_paper;
    ifstream ifs_stop;

    ifs_paper.open(getFiles(0).c_str());
    ifs_stop.open(getFiles(2).c_str());

    if (!ifs_paper)cerr << "File open failed !" << "\n";
    cout << "Open successfully !" << "\n";
    cout << "File path = " << getFiles(0) << "\n";

    if (!ifs_stop)cerr << "File open failed !" << "\n";
    cout << "Open successfully !" << "\n";
    cout << "File path = " << getFiles(2) << "\n";

    //获取语料并存入paper中
    string line;
    string paper;
    while (ifs_paper >> line)paper.append(line);

    //拆分paper中的词语
    cout << "spliting" << '\n';
    vector<string> words;
    _splitTool->cut(paper, words);

    //获取停词
    set<string> stop_word;
    while(ifs_stop >> line)stop_word.insert(line);

    //洗词
    cout << "washing" << '\n';
    vector<string> word_clean;
    word_clean.reserve(words.size()); // 预分配内存

    for(auto &i : words){
        if(stop_word.find(i) == stop_word.end()){
            word_clean.push_back(i);
        }
    }

    //统计词频
    cout << "statisticing" << '\n';
    for(auto &i : word_clean){
        auto it = std::find_if(
            _dict.begin(), _dict.end(),
            [&i](const pair<string, int> &elem)
            {
                return elem.first == i;
            });

        if (it != _dict.end())
            it->second++;
        else
            _dict.push_back({i, 1});
    }

    ifs_paper.close();
    ifs_stop.close();
}

void dictProducer::buildIndex(){
    int i = 0; // 记录下标
    for (auto elem : _dict)
    {
        string word = elem.first;
        size_t charNums = word.size() / nBytesCode(word[0]);
        for (size_t idx = 0, n = 0; n != charNums; ++idx, ++n) // 得到字符数
         {
            // 按照字符个数切割
            size_t charLen = nBytesCode(word[idx]);
            string subWord = word.substr(idx, charLen); // 按照编码格式，进行拆分
            _index[subWord].insert(i);
            idx += (charLen - 1);
        }
        ++i;
    }
}

bool dictProducer::is_initialized(const string &path){
    ifstream ifs;
    ifs.open(path);

    if(ifs)
        return true;
    return false;
}

void dictProducer::storeDict(const string &path)
{
    string dir_dict = path;
    dir_dict += "/data/dict.dat";
    string dir_index = path;
    dir_index += "/data/index.dat";

    if(!is_initialized(dir_dict)){
        ofstream ofs_dict;
        cout << "storing " << dir_dict << '\n';
        ofs_dict.open(dir_dict);

        if (!ofs_dict)
            cerr << "open or create " << dir_dict << " error" << '\n';
        
        for (auto &i : _instance->_dict)
        {
            ofs_dict << i.first << " " << i.second << '\n';
        }

        ofs_dict.close();
    }else{
        cerr << "File exist !" << "\n";
        cout << "File path = " << dir_dict << "\n";
    }
    
    if(!is_initialized(dir_index)){
        ofstream ofs_index;
        cout << "storing " << dir_index << '\n';
        ofs_index.open(dir_index);

        if (!ofs_index)
            cerr << "open or create " << dir_index << " error" << '\n';
        for (auto &i : _instance->_index)
        {
            ofs_index << i.first << ' ';
            for(auto &j : i.second){
                ofs_index << j << ' ';
            }
            ofs_index << '\n';
        }
        ofs_index.close();
    }
    else{
        cerr << "File exist !" << "\n";
        cout << "File path = " << dir_index << "\n";
    }
}

void dictProducer::showFiles()const {
    int index = 1;
    for(auto & i: _files){
        cout << "File" << index << ": " << i << '\n';
        index++;
    }
}

void dictProducer::showDict()const {
    for(auto &i : _dict){
        cout << i.first << " " << i.second << ' ' << ' ' << ' ';
    }
    cout << "Total: " << _dict.size() << '\n';
}